package bcontractor.partialmeet.operator;

import static bcontractor.builders.api.KnowledgeBaseBuilder.aKnowledgeBase;
import static bcontractor.partialmeet.RemainderSetBuilder.aRemainderSet;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

import java.util.List;

import org.junit.Before;
import org.junit.Test;

import bcontractor.api.SentenceSet;
import bcontractor.partialmeet.RemainderSet;
import bcontractor.partialmeet.RemainderSetBuilder;
import bcontractor.partialmeet.RemainderSetOperator;
import bcontractor.propositional.PropositionalSentence;
import bcontractor.propositional.SentenceParser;

/**
 * Unit test for any implementation of remainder set operator.
 * 
 * As the operation is defined precisely, any implementation should have the
 * same results on the same operands.
 * 
 * @author lundberg
 * 
 */
public abstract class ARemainderSetOperatorShould {

    private RemainderSetOperator<PropositionalSentence> resolver;

    private SentenceParser parser;

    @Before
    public void init() {
        this.resolver = this.createRemainderSetOperator();
        this.parser = new SentenceParser();
    }

    protected abstract RemainderSetOperator<PropositionalSentence> createRemainderSetOperator();

    @Test
    public void findSingleOneElementAloneOnKB() {
        SentenceSet<PropositionalSentence> base = aKnowledgeBase().with(this.parser.create("a")).build();
        PropositionalSentence alpha = this.parser.create("a");
        RemainderSet<PropositionalSentence> actual = this.resolver.eval(base, alpha);
        RemainderSet<PropositionalSentence> expected = aRemainderSet().build();
        assertThat(actual, equalTo(expected));
    }

    @Test
    public void findSingleDirectEntailment() {
        SentenceSet<PropositionalSentence> base = aKnowledgeBase().with(this.parser.createList("¬a v b", "a")).build();
        PropositionalSentence alpha = this.parser.create("b");
        RemainderSet<PropositionalSentence> actual = this.resolver.eval(base, alpha);
        RemainderSet<PropositionalSentence> expected = aRemainderSet().with(this.parser.createSet("¬a v b")).with(this.parser.createSet("a")).build();
        assertThat(actual, equalTo(expected));
    }

    @Test
    public void findManyEntailments() {
        PropositionalSentence alpha = this.parser.create("a");
        SentenceSet<PropositionalSentence> k1 = this.parser.createSet("¬b v a", "b");
        SentenceSet<PropositionalSentence> k2 = this.parser.createSet("¬b v a", "¬c v ¬d v b", "¬e v d", "c", "e");
        List<PropositionalSentence> nonrelated = this.parser.createList("j v k", "m v n v ¬q", "m v ¬n v q v j");
        SentenceSet<PropositionalSentence> base = aKnowledgeBase().with(k1).with(k2).with(nonrelated).build();
        RemainderSet<PropositionalSentence> actual = this.resolver.eval(base, alpha);
        RemainderSetBuilder<PropositionalSentence> expectedBuilder = aRemainderSet();

        expectedBuilder = expectedBuilder.with(base.without(this.parser.create("¬b v a")));
        expectedBuilder = expectedBuilder.with(base.without(this.parser.createSet("b", "¬c v ¬d v b")));
        expectedBuilder = expectedBuilder.with(base.without(this.parser.createSet("b", "¬e v d")));
        expectedBuilder = expectedBuilder.with(base.without(this.parser.createSet("b", "c")));
        expectedBuilder = expectedBuilder.with(base.without(this.parser.createSet("b", "e")));
        RemainderSet<PropositionalSentence> expected = expectedBuilder.build();
        assertThat(actual, is(equalTo(expected)));
    }

    @Test
    public void findEmptyKernelIfBaseDoesntEntail() {
        PropositionalSentence alpha = this.parser.create("a");
        List<PropositionalSentence> k1 = this.parser.createList("¬b v a");
        List<PropositionalSentence> k2 = this.parser.createList("¬b v a", "¬c v ¬d v b", "¬e v d", "c");
        List<PropositionalSentence> nonrelated = this.parser.createList("j v k", "m v n v ¬q", "m v ¬n v q v j");
        SentenceSet<PropositionalSentence> base = aKnowledgeBase().with(k1).with(k2).with(nonrelated).build();
        RemainderSet<PropositionalSentence> actual = this.resolver.eval(base, alpha);

        RemainderSet<PropositionalSentence> expected = aRemainderSet().with(base).build();
        assertThat(actual, is(equalTo(expected)));
    }

    @Test
    public void findExpressionAmongUnrelated() {
        SentenceSet<PropositionalSentence> base = aKnowledgeBase().with(this.parser.createSet("a", "b", "a v b")).build();
        PropositionalSentence alpha = this.parser.create("a");
        RemainderSet<PropositionalSentence> actual = this.resolver.eval(base, alpha);
        RemainderSet<PropositionalSentence> expected = aRemainderSet().with(this.parser.createSet("b", "a v b")).build();
        assertThat(actual, equalTo(expected));
    }
}
